Ext.data.Record = function( data, id )
{
	// if no id, call the auto id method
	this.id = (id || id === 0) ? id : Ext.data.Record.id(this);
	this.data = data || {};
};

Ext.data.Record.create = function( o )
{
	var f = Ext.extend(Ext.data.Record, {});
	var p = f.prototype;
	p.fields = new Ext.util.MixedCollection(false, function( field )
	{
		return field.name;
	});
	for( var i = 0, len = o.length; i < len; i++ )
	{
		p.fields.add(new Ext.data.Field(o[i]));
	}
	f.getField = function( name )
	{
		return p.fields.get(name);
	};
	// 返回一个子类的构造函数
	return f;
};

Ext.data.Record.PREFIX = 'ext-record';
Ext.data.Record.AUTO_ID = 1;
Ext.data.Record.EDIT = 'edit';
Ext.data.Record.REJECT = 'reject';
Ext.data.Record.COMMIT = 'commit';

Ext.data.Record.id = function( rec )
{
	rec.phantom = true;
	return
	[
	    Ext.data.Record.PREFIX, '-', Ext.data.Record.AUTO_ID++
	].join('');
};

Ext.data.Record.prototype =
{
	/**
	 * <p><b>This property is stored in the Record definition's <u>prototype</u></b></p> A MixedCollection containing the defined {@link Ext.data.Field Field}s
	 * for this Record. Read-only.
	 * 
	 * @property fields
	 * @type Ext.util.MixedCollection
	 */
	/**
	 * An object hash representing the data for this Record. Every field name in the Record definition is represented by a property of that name in this object.
	 * Note that unless you specified a field with {@link Ext.data.Field#name name} "id" in the Record definition, this will <b>not</b> contain an <tt>id</tt>
	 * property.
	 * 
	 * @property data
	 * @type {Object}
	 */
	/**
	 * The unique ID of the Record {@link #Record as specified at construction time}.
	 * 
	 * @property id
	 * @type {Object}
	 */
	/**
	 * <p><b>Only present if this Record was created by an {@link Ext.data.XmlReader XmlReader}</b>.</p> <p>The XML element which was the source of the data
	 * for this Record.</p>
	 * 
	 * @property node
	 * @type {XMLElement}
	 */
	/**
	 * <p><b>Only present if this Record was created by an {@link Ext.data.ArrayReader ArrayReader} or a {@link Ext.data.JsonReader JsonReader}</b>.</p> <p>The
	 * Array or object which was the source of the data for this Record.</p>
	 * 
	 * @property json
	 * @type {Array|Object}
	 */
	/**
	 * Readonly flag - true if this Record has been modified.
	 * 
	 * @type Boolean
	 */
	dirty: false,
	editing: false,
	error: null,
	/**
	 * This object contains a key and value storing the original values of all modified fields or is null if no fields have been modified.
	 * 
	 * @property modified
	 * @type {Object}
	 */
	modified: null,
	/**
	 * <tt>true</tt> when the record does not yet exist in a server-side database (see {@link #markDirty}). Any record which has a real database pk set as its
	 * id property is NOT a phantom -- it's real.
	 * 
	 * @property phantom
	 * @type {Boolean}
	 */
	phantom: false,

	// private
	join: function( store )
	{
		/**
		 * The {@link Ext.data.Store} to which this Record belongs.
		 * 
		 * @property store
		 * @type {Ext.data.Store}
		 */
		this.store = store;
	},

	set: function( name, value )
	{

		// 如果value与原来的值相同，就返回
		var encode = Ext.isPrimitive(value) ? String : Ext.encode;
		if( encode(this.data[name]) == encode(value) )
		{
			return;
		}

		this.dirty = true;

		// 修改值，并将被修改的值存入到modified对象中
		if( !this.modified )
		{
			this.modified = {};
		}
		if( this.modified[name] === undefined )
		{
			this.modified[name] = this.data[name];
		}
		this.data[name] = value;

		// 如果没有处于编辑的状态
		if( !this.editing )
		{
			// 调用store的afterEdit方法，将record加入到自己维护的modified数组中
			// 并且触发store的update事件
			// record的modified与store的不一样的
			// 前者存入的是被set掉的破值，后者存入的是被保存的新值
			this.afterEdit();
		}
	},

	// private
	afterEdit: function()
	{
		if( this.store != undefined && typeof this.store.afterEdit == "function" )
		{
			this.store.afterEdit(this);
		}
	},

	// private
	afterReject: function()
	{
		if( this.store )
		{
			this.store.afterReject(this);
		}
	},

	// private
	afterCommit: function()
	{
		if( this.store )
		{
			this.store.afterCommit(this);
		}
	},

	/**
	 * Get the value of the {@link Ext.data.Field#name named field}.
	 * 
	 * @param {String} name The {@link Ext.data.Field#name name of the field} to get the value of.
	 * @return {Object} The value of the field.
	 */
	get: function( name )
	{
		return this.data[name];
	},

	/**
	 * Begin an edit. While in edit mode, no events (e.g.. the <code>update</code> event) are relayed to the containing store. See also:
	 * <code>{@link #endEdit}</code> and <code>{@link #cancelEdit}</code>.
	 */
	beginEdit: function()
	{
		// 正在编辑中。。。。。
		this.editing = true;
		// 将修改保存的modified数组清空
		this.modified = this.modified || {};
	},

	/**
	 * Cancels all changes made in the current edit operation.
	 */
	cancelEdit: function()
	{
		// 不编辑了，取消了
		this.editing = false;
		// 直接删除这个数组，太绝了
		delete this.modified;
	},

	/**
	 * End an edit. If any data was modified, the containing store is notified (ie, the store's <code>update</code> event will fire).
	 */
	endEdit: function()
	{
		// 不编辑了，编辑完了
		this.editing = false;
		// 如果有修改，就修改store中的modified
		if( this.dirty )
		{
			this.afterEdit();
		}
	},

	// 回滚操作
	// silent 静默的 若为true，则只修改record中的modified
	// 默认为false，还要修改store中的modified
	reject: function( silent )
	{
		// 倒退回去
		var m = this.modified;
		for( var n in m )
		{
			if( typeof m[n] != "function" )
			{
				this.data[n] = m[n];
			}
		}
		this.dirty = false;
		delete this.modified;
		this.editing = false;
		if( silent !== true )
		{
			this.afterReject();
		}
	},

	// 提交操作
	commit: function( silent )
	{
		// 将脏数据标志位设为false
		this.dirty = false;
		// 删除modified
		delete this.modified;

		// 将正在编标志位设为false
		this.editing = false;

		// 如果不是静默提交，那么通知store
		if( silent !== true )
		{
			this.afterCommit();
		}
	},

	/**
	 * Gets a hash of only the fields that have been modified since this Record was created or commited.
	 * 
	 * @return Object
	 */
	// 返回修改的数据
	getChanges: function()
	{
		var m = this.modified, cs = {};

		// 过滤掉自带的属性
		for( var n in m )
		{
			if( m.hasOwnProperty(n) )
			{
				cs[n] = this.data[n];
			}
		}
		return cs;
	},

	// private
	hasError: function()
	{
		return this.error !== null;
	},

	// private
	clearError: function()
	{
		this.error = null;
	},

	// 重新克隆一个
	copy: function( newId )
	{
		return new this.constructor(Ext.apply({}, this.data), newId || this.id);
	},

	/**
	 * Returns <tt>true</tt> if the passed field name has been <code>{@link #modified}</code> since the load or last commit.
	 * 
	 * @param {String} fieldName {@link Ext.data.Field.{@link Ext.data.Field#name}
	 * @return {Boolean}
	 */
	isModified: function( fieldName )
	{
		return !!(this.modified && this.modified.hasOwnProperty(fieldName));
	},

	/**
	 * By default returns <tt>false</tt> if any {@link Ext.data.Field field} within the record configured with
	 * <tt>{@link Ext.data.Field#allowBlank} = false</tt> returns <tt>true</tt> from an {@link Ext}.{@link Ext#isEmpty isempty} test.
	 * 
	 * @return {Boolean}
	 */
	isValid: function()
	{
		return this.fields.find(function( f )
		{
			return (f.allowBlank === false && Ext.isEmpty(this.data[f.name])) ? true : false;
		}, this) ? false : true;
	},

	/**
	 * <p>Marks this <b>Record</b> as <code>{@link #dirty}</code>. This method is used interally when adding <code>{@link #phantom}</code> records to a
	 * {@link Ext.data.Store#writer writer enabled store}.</p> <br><p>Marking a record <code>{@link #dirty}</code> causes the phantom to be returned by
	 * {@link Ext.data.Store#getModifiedRecords} where it will have a create action composed for it during {@link Ext.data.Store#save store save} operations.</p>
	 */
	markDirty: function()
	{
		this.dirty = true;
		if( !this.modified )
		{
			this.modified = {};
		}
		this.fields.each(function( f )
		{
			this.modified[f.name] = this.data[f.name];
		}, this);
	}
};
